﻿namespace Website.Models
{
    using System;
    using System.Collections.Specialized;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Web;

    using Data;
    using Wiki;

    public class WikiPage
    {
        public WikiPage()
            : this(String.Empty)
        {
        }

        public WikiPage(string title)
            : this(title, String.Empty, null, null, null)
        {
        }

        public WikiPage(string title, string body, Guid? uuid, DateTime? lastModified, DateTime? removed)
            : this(title, body, uuid, lastModified, removed, new NameValueCollection())
        {
        }

        private WikiPage(string title, string body, Guid? uuid, DateTime? lastModified, DateTime? removed, NameValueCollection errors)
        {
            this.Title = title;
            this.Body = body;
            this.Uuid = uuid;
            this.LastModified = lastModified;
            this.Removed = removed;
            this.Errors = errors;
        }

        public NameValueCollection Errors { get; private set; }

        public string Title { get; set; }

        public string Body { get; set; }

        public Guid? Uuid { get; set; }

        public DateTime? LastModified { get; set; }

        public DateTime? Removed { get; set; }

        public bool IsAvailable
        {
            get
            {
                return (this.Uuid.HasValue && !this.Removed.HasValue);
            }
        }

        public static WikiPage Edit(NameValueCollection form)
        {
            return Edit(HttpUtility.UrlDecode(form["title"]), HttpUtility.UrlDecode(form["body"]));
        }

        public static WikiPage Edit(string title, string body)
        {
            if (null == title)
            {
                throw new ArgumentNullException("title");
            }

            NameValueCollection errors = new NameValueCollection();

            if (0 == title.Length)
            {
                errors.Add("title", "A title is required.");
            }
            else if (!IsValidTitle(title))
            {
                errors.Add("title", "A title can only contain letters (A-Z, a-z) or numbers (0-9).");
            }

            Page page = null;

            WikiDataContext wiki = new WikiDataContext();
            var query = from p in wiki.Pages
                        where p.Title == title
                        select p;

            if ((query != null) && (query.Count<Page>() != 0)) page = query.First<Page>();

            if (page == null)
            {
                page = new Page();
                page.Title = title;
                page.Uuid = Guid.NewGuid();
                wiki.Pages.InsertOnSubmit(page);
            }

            page.Body = body;

            if (0 == errors.Count)
            {
                page.LastModified = DateTime.UtcNow;
                page.Removed = null;

                wiki.SubmitChanges();
            }

            return new WikiPage(
                    page.Title,
                    page.Body,
                    page.Uuid,
                    page.LastModified,
                    page.Removed,
                    errors
                    );
        }

        public static bool Exists(string title)
        {
            WikiPage obj = WikiPage.Query(title);

            return obj.IsAvailable;
        }

        public static WikiPage Query(string title)
        {
            if (null == title)
            {
                throw new ArgumentNullException("title");
            }

            if (0 == title.Length)
            {
                throw new ArgumentOutOfRangeException("title");
            }

            WikiPage obj = null;

            WikiDataContext wiki = new WikiDataContext();
            var query = from p in wiki.Pages
                        where p.Title == title && p.Removed == null
                        select p;

            if ((query != null) && (query.Count<Page>() != 0))
            {
                Page page = query.First<Page>();
                obj = new WikiPage(
                    page.Title,
                    page.Body,
                    page.Uuid,
                    page.LastModified,
                    page.Removed
                    );
            }

            return obj ?? new WikiPage(title);
        }

        public WikiPage Remove()
        {
            WikiPage obj = null;

            WikiDataContext wiki = new WikiDataContext();
            var query = from p in wiki.Pages
                        where p.Title == this.Title
                        select p;

            if ((query != null) && (query.Count<Page>() != 0))
            {
                Page page = query.First<Page>();
                page.Removed = DateTime.UtcNow;
                wiki.SubmitChanges();
            }

            return obj ?? new WikiPage(this.Title);
        }

        public string ToHtml()
        {
            StringBuilder buffer = null;

            if (this.Body != null)
            {
                string html = HttpUtility.HtmlEncode(this.Body);

                buffer = new StringBuilder(html.Length);
                if (html.Length != 0)
                {
                    int index = 0;
                    while (index != -1)
                    {
                        index = html.IndexOf("[[");
                        if (index == -1)
                        {
                            buffer.Append(html);
                        }
                        else
                        {
                            buffer.Append(html.Substring(0, index));
                            html = html.Substring(index + 2);
                            index = html.IndexOf("]]");
                            if (index == -1)
                            {
                                buffer.Append("[[");
                                buffer.Append(html);
                            }
                            else
                            {
                                buffer.Append(FormatAnchor(html.Substring(0, index)));
                                html = html.Substring(index + 2);
                            }
                        }
                    }
                }
            }

            return (buffer == null) ? null as string : buffer.ToString();
        }

        private static string FormatAnchor(string value)
        {
            return value.IsNullOrEmpty()
                ? value
                : String.Format(CultureInfo.InvariantCulture, "<a href=\"{0}\" rel=\"bookmark\">{1}</a>", HttpUtility.UrlEncode(value), HttpUtility.HtmlEncode(value));
        }

        public static bool IsValidTitle(string title)
        {
            bool valid = !title.IsNullOrEmpty();
            
            if (valid)
            {
                foreach (char value in title.ToLowerInvariant())
                {
                    if ('-'.Equals(value))
                    {
                    }
                    else if ((96 < (int)value) && (123 > (int)value)) // a-z
                    {
                    }
                    else if ((47 < (int)value) && (58 > (int)value)) // 0-9
                    {
                    }
                    else
                    {
                        valid = false;
                        break;
                    }
                }
            }

            return valid;
        }
    }
}